// dotnet $DOTNET_CSC_DLL -nologo -t:library -r:"../../Backend/netstandard.dll" -r:"../../Backend/System.Collections.dll" -r:"../../The Scroll of Taiwu_Data/Managed/0Harmony.dll" -r:"../../Backend/mscorlib.dll" -r:"../../Backend/GameData.dll" -r:"../../Backend/Redzen.dll" -r:"../../The Scroll of Taiwu_Data/Managed/TaiwuModdingLib.dll" -r:"../../Backend/System.Private.CoreLib.dll" -r:"../../Backend/System.Runtime.dll" -unsafe -optimize -deterministic NativeCommunicationsBackend.cs *.CS -out:NativeCommunicationsBackend.dll
// dotnet "C:\Program Files\dotnet\sdk\6.0.400\Roslyn\bincore\csc.dll" -nologo -t:library -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.17\System.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.17\System.Collections.dll" -r:"..\..\..\The Scroll of Taiwu_Data\Managed\0Harmony.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.17\mscorlib.dll" -r:"..\..\..\Backend\GameData.dll" -r:"..\..\..\The Scroll of Taiwu_Data\Managed\TaiwuModdingLib.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.17\System.Private.CoreLib.dll" -r:"C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.17\System.Runtime.dll" -optimize -deterministic Backend.cs Utils.CS -out:Backend.dll
// 上面这行指令是我编译Backend.cs时候用的。

// 声明：
// 这个代码文件是太吾绘卷正式版Mod，采用双许可发布
// 在使用这个代码文件（以及其编译得到的程序）时，你应当选择AGPL-v3协议进行发布，但螺舟工作室《太吾绘卷》制作组不受此限制。
// 螺舟工作室《太吾绘卷》制作组可以使用任何非排他（因为排他地独享这段代码与代码已经使用的AGPL-v3协议冲突）的方式使用，修改或者编译这段代码。在螺舟工作室《太吾绘卷》制作组修改这段代码之后，他们可以选择以任意许可协议进行发布。
// （这么写可以了吧……反正上面这段话的目的只是我拿这段代码找螺舟麻烦，以方便现在的螺舟使用这段代码的）
// 反正我最希望的就是螺舟把这段代码吃掉，这样Modder就可以使用mod自行写前后端通信的mod了。

/* Mod使用方法：
前端，在任意位置调用 GameData.GameDataBridge.GameDataBridge.AddMethodCall(-1,65535,65535,ModId,"方法名",其他至多6个参数)
在这里，ModId可以在执行
    GameData.Domains.Mod.ModInfo modInfo;
    ((Dictionary<string, GameData.Domains.Mod.ModInfo>)(typeof(ModManager).GetField("_localMods",(BindingFlags)(-1)))).TryGetValue(modIdStr, out modInfo);
之后使用
    modInfo.ModId
获取本Mod的后端信息，大概螺舟将来可能会提供的其他方法来获取Mod信息。
而这个方法会调用后端的名为 "方法名" 的方法，此方法应当具有如下签名：
    public int 方法名 (GameData.GameDataBridge.Operation operation, GameData.Utilities.RawDataPool argDataPool, GameData.Utilities.RawDataPool returnDataPool, GameData.Common.DataContext context)
小声说一句，不会有人不知道要把“方法名”换成真实方法的名字吧……
如果传递参数数量不为0，你想要正确处理数据的反序列化，处理方法是定义相应变量，类初始化为null，结构体初始化为defalut(结构体名)
序列化非常简单，在初始化
    int argsOffset = operation.ArgsOffset;
之后，执行
    声明相应变量
    argsOffset += GameData.Serializer.Serializer.Deserialize(argDataPool, argsOffset, ref 相应变量);
即可读取CallMethod序列化之后的数据。
程序执行时，如果需要，可以将返回值序列化存储进returnDataPool，以供callback调用。如果不需要通知前端，返回负数，不需要序列化但需要通知前端，返回0，否则返回序列化长度。

Mod作者往往需要自行处理返回值，此时你应当使用
SingletonObject.getInstance<AsynchMethodDispatcher>().AsynchMethodCall(65535,65535,ModId,"方法名",其他至多6个参数,回调函数);
完成调用并处理返回值。

几个简单的例子：
前端调用后端Send0方法，不传参数
前端：
    public override void OnModSettingUpdate(){
        GameData.Domains.Mod.ModInfo modInfo;
        ((Dictionary<string, GameData.Domains.Mod.ModInfo>)(typeof(ModManager).GetField("_localMods",(BindingFlags)(-1)))).TryGetValue(modIdStr, out modInfo);
        // 普通call，此时方法最好不要有返回值
        GameData.GameDataBridge.GameDataBridge.AddMethodCall(-1,65535,65535,modInfo.ModId,"SendReturn");
        // 带回调的call
        SingletonObject.getInstance<AsynchMethodDispatcher>().AsynchMethodCall(65535,65535,modInfo.ModId,"AddReturnTitleAndString",2,3,delegate(int offset, GameData.Utilities.RawDataPool dataPool)=>{
            string ret1=null;
            string ret0=null;
            // 需要注意，反序列化时候，返回变量的顺序与序列化时顺序恰好相反。
            // 当然，我不建议一次序列化返回过多的数据

            offset-=Serializer.Deserialize(dataPool, offset, ref ret1);
            offset-=Serializer.Deserialize(dataPool, offset, ref ret0);

            DialogCmd dialogCmd4 = new DialogCmd {
                Title = ret0
                Content = ret1,
                Type = 1,
                Yes = delegate(){}
            };
            UIElement.Dialog.SetOnInitArgs(FrameWork.EasyPool.Get<FrameWork.ArgumentBox>().SetObject("Cmd", dialogCmd4));
            UIManager.Instance.ShowUI(UIElement.Dialog);
        });
    }
后端：
[TaiwuModdingLib.Core.Plugin.PluginConfig("ReceiveDemo", "Neutron3529", "0.1.0")]
public class ReceiveDemo : TaiwuModdingLib.Core.Plugin.TaiwuRemakeHarmonyPlugin {
    public static int counter=0; // 我们可以定义一些类变量来辅助计算/存储中间结果
    public int SendReturn (GameData.GameDataBridge.Operation operation, GameData.Utilities.RawDataPool argDataPool, GameData.Utilities.RawDataPool returnDataPool, GameData.Common.DataContext context){
        GameData.Utilities.AdaptableLog.Info("mod ("+modId.FileId+","+modId.Version+","+modId.Source+")收到了SendReturn方法的调用请求，这是第"+(++counter)+"次调用");
    }
    // 需保证调用的函数是类ReceiveDemo的方法。
    public int AddReturnTitleAndString (GameData.GameDataBridge.Operation operation, GameData.Utilities.RawDataPool argDataPool, GameData.Utilities.RawDataPool returnDataPool, GameData.Common.DataContext context){
        ++counter;
        int argsOffset = operation.ArgsOffset;
        int a,b;
        argsOffset += GameData.Serializer.Serializer.Deserialize(argDataPool, argsOffset, ref a);
        argsOffset += GameData.Serializer.Serializer.Deserialize(argDataPool, argsOffset, ref b);
        GameData.Utilities.AdaptableLog.Info("mod ("+modId.FileId+","+modId.Version+","+modId.Source+")收到了AddReturnTitleAndString方法的调用请求，输入数据为"+a+"和"+b+"，答案为"+(a+b));
        int num=GameData.Serializer.Serializer.Serialize("答案是"+(a+b), returnDataPool);
        num+=GameData.Serializer.Serializer.Serialize("mod ("+modId.FileId+","+modId.Version+","+modId.Source+")收到了AddReturnTitleAndString方法的调用请求，输入数据为"+a+"和"+b+"，答案为"+(a+b), returnDataPool);
        return num;
    }
}
*/


using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using HarmonyLib;
using Utils;
namespace BackendCommunications;
[TaiwuModdingLib.Core.Plugin.PluginConfig("BackendCommunications","Neutron3529","0.1.0")]
public class BackendCommunications : TaiwuModdingLib.Core.Plugin.TaiwuRemakePlugin {
    // public override void Initialize()=>this.HarmonyInstance = new RobustHarmonyInstance(this.GetGuid());
    public override void Dispose()=>this.HarmonyInstance.UnpatchSelf();
    public static void logger(string s)=>GameData.Utilities.AdaptableLog.Info(s);
    public static void logwarn(string s)=>GameData.Utilities.AdaptableLog.Warning("<color=#FFFF00>"+s+"</color>",true);
    public RobustHarmonyInstance HarmonyInstance;
    public override void Initialize(){
        this.HarmonyInstance = new RobustHarmonyInstance(this.GetGuid());
        this.HarmonyInstance.PatchAll(typeof(ProcessMethodCallPatcher));
    }
    [HarmonyPatch(typeof(GameData.GameDataBridge.GameDataBridge),"ProcessMethodCall")]
    public class ProcessMethodCallPatcher {
        public static Type[] args=new Type[]{typeof(GameData.GameDataBridge.Operation), typeof(GameData.Utilities.RawDataPool), typeof(GameData.Utilities.RawDataPool), typeof(GameData.Common.DataContext)};
        public static bool Prefix(GameData.GameDataBridge.Operation operation, GameData.Utilities.RawDataPool argDataPool, GameData.Common.DataContext context, ref GameData.GameDataBridge.NotificationCollection ____pendingNotifications){
            if(operation.DomainId==65535 && operation.MethodId==65535){
                int argsOffset = operation.ArgsOffset;
                GameData.Domains.Mod.ModId modId = default(GameData.Domains.Mod.ModId);
                string methodName=null;
                argsOffset += GameData.Serializer.Serializer.Deserialize(argDataPool, argsOffset, ref modId);
                argsOffset += GameData.Serializer.Serializer.Deserialize(argDataPool, argsOffset, ref methodName);

                List<TaiwuModdingLib.Core.Plugin.TaiwuRemakePlugin> plugins;
                TaiwuModdingLib.Core.Plugin.TaiwuRemakePlugin plugin=null;
                MethodInfo method=null;
                if(((Dictionary<GameData.Domains.Mod.ModId,List<TaiwuModdingLib.Core.Plugin.TaiwuRemakePlugin>>)(typeof(GameData.Domains.Mod.ModDomain).GetField("LoadedPlugins",(BindingFlags)(-1)).GetValue(null))).TryGetValue(modId,out plugins)) {
                    foreach(var plug in plugins){
                        method=plug.GetType().GetMethod(methodName, args);
                        if(method!=null){
                            plugin=plug;
                            break;
                        }
                    }
                } else {
                    logwarn("[Mod Communication]: 无法在<color=#FF0000>后端</color>找到modId为("+modId.FileId+","+modId.Version+","+modId.Source+")的Mod，请检查ModId是否发生了更改。");
                    return false;
                }
                int returnOffset=-1;

                if(plugin==null){
                    logwarn("[Mod Communication]: 无法在<color=#FF0000>后端</color>modId为("+modId.FileId+","+modId.Version+","+modId.Source+")的Mod中找到函数public int "+methodName+"(GameData.GameDataBridge.Operation operation, GameData.Utilities.RawDataPool argDataPool, GameData.Utilities.RawDataPool returnDataPool, GameData.Common.DataContext context), 请确保Mod已正确加载");
                } else {
                    operation.ArgsOffset=argsOffset;
                    returnOffset=(int)method.Invoke(plugin, new object[]{operation, argDataPool, ____pendingNotifications.DataPool, context});

                    if (returnOffset >= 0) {
                        ____pendingNotifications.Notifications.Add(GameData.GameDataBridge.Notification.CreateMethodReturn(operation.ListenerId, operation.DomainId, operation.MethodId, returnOffset));
                    }
                }
                return false;
            }
            return true;
        }
    }
}
